import { DynamicModule, Global, Module, Provider } from '@nestjs/common'
import { DISCORDOO_OPTIONS } from './discordoo.constants'
import { DiscordooService } from './discordoo.service'
import { 
  DiscordooModuleAsyncOptions, 
  DiscordooModuleOptions, 
  DiscordooModuleOptionsFactory,
} from './interfaces/discordoo-options.interface'

@Module({
  providers: [ DiscordooService ],
  exports: [ DiscordooService ],
})
@Global()
export class DiscordooModule {
  public static forRoot(config: DiscordooModuleOptions): DynamicModule {
    return {
      module: DiscordooModule,
      providers: [
        {
          provide: DISCORDOO_OPTIONS,
          useValue: config,
        },
      ],
    }
  }

  public static forRootAsync(options: DiscordooModuleAsyncOptions): DynamicModule {
    return {
      module: DiscordooModule,
      imports: options.imports,
      providers: this.createAsyncProviders(options),
    }
  }

  private static createAsyncProviders(options: DiscordooModuleAsyncOptions): Provider[] {
    if (options.useExisting || options.useFactory) {
      return [ this.createAsyncOptionsProvider(options) ]
    }

    return [
      this.createAsyncOptionsProvider(options),
      {
        useClass: options.useClass!,
        provide: options.useClass!,
      },
    ]
  }

  private static createAsyncOptionsProvider(options: DiscordooModuleAsyncOptions): Provider {
    if (options.useFactory) {
      return {
        provide: DISCORDOO_OPTIONS,
        useFactory: options.useFactory,
        inject: options.inject || [],
      }
    }

    return {
      provide: DISCORDOO_OPTIONS,
      useFactory: async (optionsFactory: DiscordooModuleOptionsFactory) => (
        await optionsFactory.createDiscordooOptions()
      ),
      inject: [ (options.useExisting || options.useClass)! ],
    }
  }
}